//
//  Swiccmsd.swift
//  YPWatermarkCamera
//
//  Created by 马涛 on 2022/6/24.
//

import Foundation
import Moya
import RxSwift
import HandyJSON

class WmcNetwork {

  /// 请求
  public static func request(_ requestConfig: RequestConfig) -> Observable<Response> {
      return RxSwift.Observable.create { subscribe in
      MoyaProvider<RequestConfig>(endpointClosure: requestConfig.endpointClosure,
                       requestClosure: requestConfig.requestClosure,
                       stubClosure: requestConfig.stubClosure,
                       callbackQueue: requestConfig.callbackQueue,
                       session: requestConfig.session,
                       plugins: requestConfig.plugins,
                       trackInflights: requestConfig.trackInflights)
        .request(requestConfig, callbackQueue: requestConfig.callbackQueue, progress: requestConfig.progress, completion: { result in
          switch result {
          case let .success(response):
            subscribe.onNext(response)
            subscribe.onCompleted()
          case let .failure(error):
            subscribe.onError(error)
            subscribe.onCompleted()
            break
          }
        })
      return Disposables.create()
    }
  }
}
// MARK: - 配置扩展（所有的网络请求配置写在这里面）
extension WmcNetwork.RequestConfig {

  typealias RequestConfig = WmcNetwork.RequestConfig

  /// 通用请求
  static func general(method: Moya.Method, path: String, params: [String : Any]? = nil) -> RequestConfig {
    // 基础url
      let baseURL = URL(string: WmcYupaoRequest.init().host)!
    // 请求任务
    let task = Task.requestParameters(parameters: params ?? [:], encoding: URLEncoding.default)
    // 请求头
      var headers: [String: String] {
          var _header: [String: String] = [:]
          for dic in WmcYupaoRequest.init().clientInfo {
              _header[dic.key] = dic.value
          }
          for dic in WmcYupaoRequest.init().userInfo {
              _header[dic.key] = dic.value
          }
          return _header
      }

    let requestConfig = RequestConfig(baseURL: baseURL, method: method, path: path, task: task, headers: headers, timeoutInterval: 10)

    #if DEBUG
    requestConfig.enableLog = true
    #endif

    return requestConfig
  }
}


// MARK: - 额外类扩展
extension WmcNetwork {

  // MARK: 请求配置
  class RequestConfig: TargetType {

    /// 基础URL
    public var baseURL: URL
    /// 请求方法
    public var method: Moya.Method
    /// 请求路径
    public var path: String
    /// 请求类型，Data/Downlaod/Upload
    public var task: Task
    /// 请求头
    public var headers: [String : String]?
    /// 超时时长
    public var timeoutInterval: TimeInterval
    /// callbackQueue
    public var callbackQueue: DispatchQueue?
    /// 进度闭包
    public var progress: ProgressBlock? = .none
    /// 模拟数据
    public var sampleData: Data = "".data(using: String.Encoding.utf8)!

    public var endpointClosure: MoyaProvider<RequestConfig>.EndpointClosure = MoyaProvider.defaultEndpointMapping
    public var requestClosure: MoyaProvider<RequestConfig>.RequestClosure = MoyaProvider<RequestConfig>.defaultRequestMapping
    /// 模拟网络请求
    public var stubClosure: MoyaProvider<RequestConfig>.StubClosure = MoyaProvider.neverStub
    public var session: Session = MoyaProvider<RequestConfig>.defaultAlamofireSession()
    /// 插件
    public var plugins: [PluginType] = []
    public var trackInflights: Bool = false

    /// 开始时间，用于请求耗时计算
    public var startTime: Date = Date()

    /// 启用日志
    public var enableLog: Bool = false {
      didSet {
        if enableLog {
          self.plugins.append(logPlugin)
        }else {
          self.plugins.removeAll { plugin in
            plugin is NetworkLogger
          }
        }
      }
    }

    /// 日志组件
    public lazy var logPlugin: NetworkLogger = {
      return NetworkLogger()
    }()

    /// 初始化
    public init(baseURL: URL,
       method: Moya.Method,
       path: String,
       task: Task,
       headers: [String : String]?,
       timeoutInterval: TimeInterval,
       callbackQueue: DispatchQueue? = .main) {
      self.baseURL = baseURL
      self.method = method
      self.path = path
      self.task = task
      self.headers = headers
      self.timeoutInterval = timeoutInterval
      self.requestClosure = timeoutClosure()
      self.callbackQueue = callbackQueue
    }

    // 超时中间件
    open func timeoutClosure() -> ((_ endpoint: Endpoint, _ closure: MoyaProvider<RequestConfig>.RequestResultClosure) -> Void) {
      let timeoutClosure = {(endpoint: Endpoint, closure: MoyaProvider<RequestConfig>.RequestResultClosure) -> Void in
        if var urlRequest = try? endpoint.urlRequest() {
          urlRequest.timeoutInterval = self.timeoutInterval
          closure(.success(urlRequest))
        } else {
          closure(.failure(MoyaError.requestMapping(endpoint.url)))
        }
      }
      return timeoutClosure
    }

  }


  // MARK: 网络日志
  class NetworkLogger: PluginType {
    public func willSend(_ request: RequestType, target: TargetType) {
      let log = self.generateRequestLog(request, target: target)
        WmcLogManager.Log(log)
    }

    public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
      let log = self.generateResponseLog(result, target: target)
        WmcLogManager.Log(log)
    }

    func generateRequestLog(_ request: RequestType, target: TargetType) -> String {
      var log = "\n"
      log += "===============💜💜💜 Request 💜💜💜 ===============\n"
      let url = "\(target.baseURL.absoluteString)\(target.path)"
      log += "URL: \(url)\n"
      log += "Host: \(target.baseURL.absoluteString)\n"
      log += "Path: \(target.path)\n"
      log += "Method: \(target.method.rawValue)\n"
      log += "Header: \(target.headers ?? [:])\n"
      let formatter = DateFormatter()
      formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
      log += "Time: \(formatter.string(from: Date()))\n"
      log += "Task: \(target.task)\n"
      log += "===============💜💜💜 End 💜💜💜===============\n"
      return log
    }

    func generateResponseLog(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> String {
      var log = "\n"
      do {
        let response = try result.get()
        log += "===============💚💚💚 Response 💚💚💚===============\n"
        let url = "\(target.baseURL.absoluteString)\(target.path)"
        log += "URL: \(url)\n"
        if let requestConfig = target as? RequestConfig {
          let usedTime = Date().timeIntervalSince1970 - requestConfig.startTime.timeIntervalSince1970
          let usedTimeStr = String(format: "%.1f", usedTime)
          log += "UsedTime: \(usedTimeStr) s\n"
        }
        let data = String(data: response.data, encoding: .utf8)
        log += data != nil ? "Data: \(data!) \n" : "Data: \n"
        log += "===============💚💚💚 End 💚💚💚===============\n"
      }catch {
        if let error = error as? MoyaError {
          log += "===============👿👿👿 Response 👿👿👿===============\n"
          let url = "\(target.baseURL.absoluteString)\(target.path)"
          log += "URL: \(url)\n"
          log += "Error: \(error.errorDescription ?? "") \n"
          log += "Code: \(error.errorCode) \n"
          if let requestConfig = target as? RequestConfig {
            let usedTime = Date().timeIntervalSince1970 - requestConfig.startTime.timeIntervalSince1970
            let usedTimeStr = String(format: "%.1f", usedTime)
            log += "UsedTime: \(usedTimeStr) s\n"
          }
          log += "=============== End ===============\n"
        }
      }
      return log
    }

  }

}

// MARK: - 抛出错误协议
protocol WmcErrorEmittable {
  func throwError() -> Error?
}

// MARK: - 扩展解析方法
public extension Observable where Element == Response {

  /// 转字典
  func mapDictionary() -> Observable<[String : Any]> {
    return self.flatMap { element in
        return RxSwift.Observable<[String : Any]>.create { observer in
        if let dict = try? JSONSerialization.jsonObject(with: element.data, options: .mutableContainers) as? [String: Any] {
          observer.onNext(dict)
        }else {
          observer.onError(MoyaError.jsonMapping(element))
        }
        observer.onCompleted()
        return Disposables.create()
      }
    }
  }

  /// 转模型(含错误处理)
  func mapModel<T: HandyJSON>(_ type: T.Type, throwErrorEnable: Bool = true, customThrowError: ((T) -> Error?)? = nil) -> Observable<T> {
    return self.flatMap { element in
      return RxSwift.Observable<T>.create { observer in
        if let jsonString = String(data: element.data, encoding: .utf8),
          let model = JSONDeserializer<T>.deserializeFrom(json: jsonString) {

          // 自定义错误抛出, throwErrorEnable不再生效
          if let customThrowError = customThrowError {
            if let error = customThrowError(model) {
              observer.onError(error)
            }else {
              observer.onNext(model)
            }
          }else {
            // 判断是否遵循WmcErrorEmittable协议
            if throwErrorEnable{
              if let WmcErrorEmittable = model as? WmcErrorEmittable {
                if let error = WmcErrorEmittable.throwError() {
                  observer.onError(error)
                }else {
                  observer.onNext(model)
                }
              }else {
                fatalError("Error: If throwErrorEnable is true, the model needs to implement the WmcErrorEmittable protocol!")
              }
            }else {
              observer.onNext(model)
            }
          }
        }else {
          observer.onError(MoyaError.jsonMapping(element))
        }

        observer.onCompleted()
        return Disposables.create()
      }
    }
  }
}

